#include "ObjMesh.h"
#include "TriangleList.h"
#include "DAEFloat.h"
#include "Skeleton.h"
#include "Cone.h"
#include "TextWriter.h"

ObjMesh::ObjMesh():
m_pVertexList(NULL),
m_AABBMesh(NULL),
m_pSkeleton(NULL)
{
}

ObjMesh::ObjMesh(VertexList* vl,IndexedGeometry* ig)
	:
	m_pVertexList(vl),
	m_AABBMesh(NULL),
	m_pSkeleton(NULL)
{
	IndexBufferMaterial* ibm = new IndexBufferMaterial(ig);
	m_IndexBuffers.push_back(ibm);
}

ObjMesh::ObjMesh(VertexList * vl,IndexedGeometry * ig, Material* pMaterial)
	:
	m_pVertexList(vl),
	m_AABBMesh(NULL),
	m_pSkeleton(NULL)
{
	IndexBufferMaterial* ibm = new IndexBufferMaterial(ig,pMaterial);
	m_IndexBuffers.push_back(ibm);
}

ObjMesh::~ObjMesh(void)
{
	SAFE_DELETE(m_pVertexList);
	vector<IndexBufferMaterial*>::const_iterator it;
	for (it = m_IndexBuffers.begin();it!=m_IndexBuffers.end();++it)
		delete *it;
	SAFE_DELETE(m_AABBMesh);
	//SAFE_DELETE(m_pSkeleton);
}

void ObjMesh::AddSemanticToVertexList(SemanticDescriptor sd, FXValue* value)
{
	m_pVertexList->AddSemantic(sd,value);
}

void ObjMesh::SetSkeleton(Skeleton* skeleton){
		m_pSkeleton = skeleton;
		
}

void ObjMesh::SetName(tstring name){
	m_Name = name;
}

tstring ObjMesh::GetName(){
	return m_Name;
}

bool ObjMesh::NeedsTangents()
{
	vector<IndexBufferMaterial*>::const_iterator it;
	for ( it = m_IndexBuffers.begin(); it != m_IndexBuffers.end();++it)
	{
		IndexBufferMaterial* ibm = *it;
		if (ibm->GetMaterial() ==NULL)
			continue;
		else{
			if ( ibm->GetMaterial()->IsNormalMapTextureSet())
				return true;
		}
	}
	return false;
}

void ObjMesh::Retesselate(Cone * c)
{
	vector<IndexBufferMaterial*>::const_iterator it;
	int posOffset = m_pVertexList->GetOffset(POSITION);
	for ( it = m_IndexBuffers.begin(); it != m_IndexBuffers.end();++it)
	{
		IndexBufferMaterial* ibm = *it;
		IndexedGeometry* indices = ibm->GetIndexBuffer();
		if ( indices->GetPrimitiveType() == TRIANGLES )
		{
			TriangleList* triList = dynamic_cast<TriangleList*>(indices);
			if ( triList != NULL )
			{
				int numTriangles = triList->GetCurrentNrOfTriangles();
				for (int i = 0; i < numTriangles; ++i )
				{
					DAEFloat3 p1,p2,p3;
					int i1,i2,i3;
					triList->GetIndexedFace(i, &i1,&i2,&i3);
					RetesselateTriangle(c,triList,posOffset,i,i1,i2,i3);
				}
			}
		}
	}
	TextWriter* tw = new TextWriter(_T("d:/data/meshreport.obj"));
	tw->Open();
	tstring header = _T("#mesh retessellation report\n"); 
	tw->WriteString(header);
	tstring v = _T("v ");
	tstring vn = _T("vn ");
	tstring space = _T(" ");
	for ( int i = 0 ; i < m_pVertexList->GetSize(); ++i )
	{
		DAEFloat3 position;
		m_pVertexList->GetVertexData(i,POSITION,&position);
		tw->WriteString(v);
		tw->WriteFloat(position.x);
		tw->WriteString(space);
		tw->WriteFloat(position.y);
		tw->WriteString(space);
		tw->WriteFloat(position.z);
		tw->WriteEndl();
	}

	for ( int i = 0 ; i < m_pVertexList->GetSize(); ++i )
	{
		DAEFloat3 normal;
		m_pVertexList->GetVertexData(i,NORMAL,&normal);
		tw->WriteString(vn);
		tw->WriteFloat(normal.x);
		tw->WriteString(space);
		tw->WriteFloat(normal.y);
		tw->WriteString(space);
		tw->WriteFloat(normal.z);
		tw->WriteEndl();
	}

	tstring group = _T("g test\n");
	tw->WriteString(group);
	tstring face =  _T("f ");
	IndexBufferMaterial* pMaterial = *m_IndexBuffers.begin();
	IndexedGeometry* ig = pMaterial->GetIndexBuffer();
	if ( ig->GetPrimitiveType() == TRIANGLES )
	{
		TriangleList* triList = dynamic_cast<TriangleList*>(ig);
		for (int i = 0; i < triList->GetCurrentNrOfTriangles(); ++i )
		{
			DAEFloat3 p1,p2,p3;
			int i1,i2,i3;
			triList->GetIndexedFace(i, &i1,&i2,&i3);
			tw->WriteString(face);
			tw->WriteInt(i1+1);
			tw->WriteString(space);
			tw->WriteInt(i2+1);
			tw->WriteString(space);
			tw->WriteInt(i3+1);
			tw->WriteEndl();
		}
	}
	tw->Close();
}

void ObjMesh::RetesselateTriangle(Cone* c,TriangleList* triList,int posOffset, int currentTriangleIndex, int i1,  int i2,  int i3 )
{
	DAEFloat3 p1,p2,p3;
	m_pVertexList->GetVertexData(i1,posOffset,&p1);
	m_pVertexList->GetVertexData(i2,posOffset,&p2);
	m_pVertexList->GetVertexData(i3,posOffset,&p3);

	float l1,l2;
	if ( c->IntersectTriangle(p1,p2,p3,l1,l2) )
	{
		if ( l1 > 0.001 && l2 > 0.001 && l1 < 0.999 && l2 < 0.999)
		{
			DAEFloat3 center =  p1 + (p2-p1)*l1 + (p3-p1)*l2;
			int index = m_pVertexList->AddInterpolatedVertex(i1,i2,i3, 1-l1-l2, l1, l2 );
			triList->SetIndexedFace(currentTriangleIndex, i1,i2,index);
			int tri2Index = triList->AddIndexedFace(i2,i3,index);
			int tri3Index = triList->AddIndexedFace(i3,i1,index);

			RetesselateTriangle(c,triList,posOffset,currentTriangleIndex,i1,i2,index);
			RetesselateTriangle(c,triList,posOffset,tri2Index,i2,i3,index);
			RetesselateTriangle(c,triList,posOffset,tri3Index,i3,i1,index);
			return;
		}
	}

	if ( c->IntersectsTriangle2(p1,p2,p3) )
	{
		float e1t1=0,e1t2=0,e2t1=0,e2t2=0,e3t1=0,e3t2=0;
		int nrE1pts = c->IntersectLine(p1,p2,e1t1,e1t2);
		int nrE2pts = c->IntersectLine(p2,p3,e2t1,e2t2);
		int nrE3pts = c->IntersectLine(p3,p1,e3t1,e3t2);

		if ( nrE1pts + nrE2pts + nrE3pts > 0 )
		{
			int cindex = m_pVertexList->AddInterpolatedVertex(i1,i2,i3,1.0f/3.0f,1.0f/3.0f,1.0f/3.0f );
			int indices[10];
			DAEFloat3 points[6];
			indices[0] = i1;
			int index = 1;
			int numvertices = 0;
			DAEFloat3 helper;
			//first edge
			if ( nrE1pts > 0 ){
				indices[index++] = m_pVertexList->AddInterpolatedVertex(i1,i2,e1t1);
				m_pVertexList->GetVertexData(indices[index-1],posOffset,&helper);
				points[numvertices] = helper;
				++numvertices;
			}
			if ( nrE1pts > 1 ){
				indices[index++] = m_pVertexList->AddInterpolatedVertex(i1,i2,e1t2);
				m_pVertexList->GetVertexData(indices[index-1],posOffset,&helper);
				points[numvertices] = helper;
				++numvertices;
			}
			indices[index++]=i2;
			//second edge
			if ( nrE2pts > 0 ){
				indices[index++] = m_pVertexList->AddInterpolatedVertex(i2,i3,e2t1);
				m_pVertexList->GetVertexData(indices[index-1],posOffset,&helper);
				points[numvertices] = helper;
				++numvertices;
			}
			if ( nrE2pts > 1 ){
				indices[index++] = m_pVertexList->AddInterpolatedVertex(i2,i3,e2t2);
				m_pVertexList->GetVertexData(indices[index-1],posOffset,&helper);
				points[numvertices] = helper;
				++numvertices;
			}
			indices[index++]=i3;
			// third edge
			if ( nrE3pts > 0 ){
				indices[index++] = m_pVertexList->AddInterpolatedVertex(i3,i1,e3t1);
				m_pVertexList->GetVertexData(indices[index-1],posOffset,&helper);
				points[numvertices] = helper;
				++numvertices;
			}
			if ( nrE3pts > 1 ){
				indices[index++] = m_pVertexList->AddInterpolatedVertex(i3,i1,e3t2);
				m_pVertexList->GetVertexData(indices[index-1],posOffset,&helper);
				points[numvertices] = helper;
				++numvertices;
			}
			indices[index++]=i1;
			
			// create the triangles
			triList->SetIndexedFace(currentTriangleIndex,indices[0],indices[1],cindex);
			for (int i = 1;i < (index-1); ++i){
				triList->AddIndexedFace(indices[i],indices[i+1],cindex);
			}

			float l1 , l2;
			

			DAEFloat3 triangleNormal;
			DAEFloat3 e1 = p2-p1;
			DAEFloat3 e2 = p3-p1;
			e1.Cross(triangleNormal,e2);
			DAEFloat3 coneCenter;
			if ( c->AxisIntersectPlane(coneCenter, p1,triangleNormal) )
			{
				//DAEFloat3 triCenter = p1 / 3.0f + p2 / 3.0f + p3 / 3.0f;
				float t1, t2;
				DAEFloat3 mean;
				for (int i = 0 ;  i < numvertices; ++ i)
				{
					DAEFloat3 diff = points[i] - coneCenter;
					diff.Normalize();
					mean += coneCenter + diff;
				}
				mean/= numvertices;
				int nrpts = c->IntersectLine(coneCenter,mean,t1,t2,false);
				if ( nrpts >= 1 ){
					float t = t1;
					if ( t2 > t1 )
						t = t2;
					DAEFloat3 center = coneCenter + (mean-coneCenter)*t;
					DAEFloat3 bcoords = DAEFloat3::BarycentricCoordinates(center,p1,p2,p3);
					float sum = bcoords.x + bcoords.y + bcoords.z ;
					if ( bcoords.x > 0  && bcoords.x < 1 && 
						 bcoords.y > 0  && bcoords.y < 1 && 
						sum < 1.00001 && sum > 0.99999 )
					{
						m_pVertexList->AddVertexData(cindex,POSITION,&center);
						m_pVertexList->SetInterpolatedVertex(cindex,i1,i2,i3,bcoords.x,bcoords.y,bcoords.z);
					}
				}
			}		
		}
	}
}